#include <stdio.h>  //for printf
#include <stdlib.h> //for malloc, free
#include <string.h> //for strcpy, etc...
#include <stdarg.h> //for va_list, etc...
#include <unistd.h>
#include <errno.h>
#include <ctype.h>
#include <time.h>
#include "albero.h"

#define error_result thread_result_t *
#define error_declare() thread_result_t * res = NULL
#define error_exit(code, msg, arg...) {res = error(code, msg, ## arg); goto exit;}
#define error_return() if (res) return res; else return success(); 
#define malloc_check(var, type, len_var) var = (type *)malloc(sizeof(type)*len_var); if (var == NULL) error_exit(1, "malloc " #var)
#define fopen_check(var, name, mode) var = fopen(name, mode); if (dev == NULL) error_exit(1, "fopen " #name " " #mode)



unsigned long visit(char *in, char *out, unsigned long lim, int mode) {
    tree * tr = NULL;
    char *tmp;
    unsigned long start = 0;
    int dup = 0;
    char *start_at = NULL;

    tmp = (char *)malloc(sizeof(char)*lim);
    tr = create(NULL,NULL);

    copy_words(in, tmp, lim, tr, &start_at, 1);

    switch (mode) {
	case 1:
	    inorder(tr, out, &lim, &start);
	    break;
	case 2:
	    dup = preorder(tr, out, &lim, &start);
	    break;
	case 3:
	    postorder(tr, out, &lim, &start);
	    break;
	case 4:
	    invorder(tr, out, &lim, &start);
	    break;
	default:
	    break;

    }
    free(tmp);
    tree_free(tr, 0);

    return start;
}

typedef struct thread_result {
    int err;
    char *msg;
} thread_result_t;

thread_result_t * success() {
    thread_result_t *res = (thread_result_t *)malloc(sizeof(thread_result_t));
    if (res == NULL)
	return NULL;

    res->err=0;
    res->msg = (char *)malloc(sizeof(char)*50);
    if (res->msg)
	snprintf(res->msg, sizeof(char)*50, "success");
    return res;
}

thread_result_t * verror(int err, const char *format, va_list argp) {
    thread_result_t *res = (thread_result_t *)malloc(sizeof(thread_result_t));
    if (res == NULL)
	goto exit;
    res->err = err;
    if (format == NULL)
	goto exit;
    res->msg = (char *)malloc(sizeof(char)*50);
    if (res->msg)
	vsnprintf(res->msg, sizeof(char)*50, format, argp);
exit:
    return res;
}

thread_result_t * error(int err, const char *format, ...) {
    va_list argp;
    va_start(argp, format);
    thread_result_t *res = verror(err, format, argp);
    va_end(argp);
    return res;
}



char randChar(){
  int n,t;
  char res;

  n=rand()%26;//generatore per la lettera
  t=rand()%3;//generatore per il numero
  
  switch(t){
  case 0: //Ã¨ una lettera minuscola
    res='a'+n;
    break;

  case 1://Ã¨ una lettera maiuscola
    res='A'+n;
    break;
    
  case 2://Ã¨ una cifra
    res='0'+(n%10);
    break;
  default:
    res = 'a';
    printf("randChar wrong case\n");
    }

return res;
}

void randString(char* string, int len){
  int l,i,count;
  
  count = 0;
  while (count < len) {
      l=(rand()%25)+1;
      if (l > len - count - 1)
	  l = len - count - 1;
      for (i = 0; i < l; i++) {
	  string[count + i] = randChar();
      }
      string[count+i] = ' ';
      count += i + 1;
  }
  string[len] = '\0';
}

error_result test(int qset, int mode) {
    error_declare();
    FILE *dev = NULL;
    char *in = NULL;
    char *out = NULL;
    char *buf = NULL;
    size_t n_write, n_read;
    int i;
    int quantum = 4000;
    unsigned long count;
    unsigned long len = qset * quantum; 
    char devname[] = "/dev/scull0";
    
    malloc_check(in, char, len+1);
    malloc_check(out, char, len+1);
    malloc_check(buf, char, len+1);
    
    srand(time(NULL));
    
    /*for (i = 0; i < qset; i++) {
    	randString(in+quantum*i, quantum);
    }*/
    randString(in,len);
    count = visit(in, buf, len, mode);

    fopen_check(dev, devname, "w");

    n_write = fwrite(in, sizeof(char), len, dev);
    printf("testing mode %d, wrote %d of %ld bytes\n", mode, n_write, len);

    fclose(dev);
    dev = NULL;

    fopen_check(dev, devname, "r");

    n_read = fread(out, sizeof(char), len, dev);
    if (n_read == count) {
	for (i = 0; i < count; i++) {
	    if (buf[i] != out[i]) {
		printf("fail: character at position %d is '%c' expected '%c'\n", i, out[i], buf[i]);
		if (i > 50)
		    printf("%s\n%s", buf+i-50, out+i-50);
		else
		    printf("%s\n%s", buf, out);
		break;
	    }
	}
	if (i == count)
	    printf("success\n");
    }
    else {
	printf("fail: read %d bytes, expected %ld\n", n_read, count);
	for (i = 0; i < n_read && i < count; i++) {
	    if (buf[i] != out[i]) {
		printf("fail: character at position %d is '%c' expected '%c'\n", i, out[i], buf[i]);
		if (i > 50)
		    printf("%s\n%s", buf+i-50, out+i-50);
		else
		    printf("%s\n%s", buf, out);
		break;
	    }
	}


    }

exit:
    free(in);
    free(out);
    free(buf);
    if (dev)
	fclose(dev);
    error_return();
}

int main(int argc, char *argv[])
{
    int qset;
    int mode;

    qset = strtoul(argv[1], NULL, 0);
    mode = strtoul(argv[2], NULL, 0);

    test(qset, mode);
    return 0;
}

